import json
import logging
import sys
import time

from Service_Deploy import DeployMain
from alibabacloud_slb20140515.client import Client as Slb20140515Client
from alibabacloud_ecs20140526.client import Client as Ecs20140526Client
from alibabacloud_ecs20140526 import models as ecs_20140526_models
from alibabacloud_tea_openapi import models as open_api_models
from alibabacloud_slb20140515 import models as slb_20140515_models
log_format = '%(asctime)s - %(filename)s[line:%(lineno)d] - %(levelname)s: %(message)s'
logging.basicConfig(level=logging.INFO, format=log_format)


# 需要虚拟服务器组的name与project_name相同 SLB默认是yinge-finance/172.16.9.147 id是lb-bp1ou6w1me6r6agxihxbw,可以在env.ini中修改
# 此脚本适合项目部署了两台或两台以上的服务
class Sample:
    def __init__(self,
                 env: str,
                 project_name: str,
                 package_name: str) -> None:
        # 读取配置文件
        with open('Env.json', 'r', encoding='utf8') as _json:
            env_json = json.load(_json)
            _json.close()
        self.Env_json = env_json
        self.Project_json = env_json["Project"][env][project_name]
        self.Ali_Cloud = env_json["Ali_cloud"]

        # load_balancer_id 是对应的SLB实例ID可以通过修改实现查询不同的虚拟服务组
        self.load_balancer_id = self.Project_json['Load_Balancer_ID']

        self.ENV = env
        self.Project_Name = project_name
        self.Package_Name = package_name

    # 创建连接认证
    def create_client(self, what_is: str):
        config = open_api_models.Config(access_key_id=self.Ali_Cloud['Access_Key_ID'],
                                        access_key_secret=self.Ali_Cloud['Access_Key_Secret'])

        # 如果传入的参数为Slb，设置为slb的url  如果传入的是Ecs设置为Ecs的url
        if what_is == 'Slb':
            config.endpoint = self.Ali_Cloud['SLB_URL']
            return Slb20140515Client(config)
        elif what_is == 'Ecs':
            config.endpoint = self.Ali_Cloud['ECS_URL']
            return Ecs20140526Client(config)

    # 查询并返回某个server_id的实例内网ip_address
    def ecs_ip(self, instance_ids: str) -> None:
        client = self.create_client('Ecs')
        describe_instances_request = ecs_20140526_models.DescribeInstancesRequest(
            region_id=self.Ali_Cloud['Region_ID'],
            instance_ids=f'["{instance_ids}"]')
        result_ecs = client.describe_instances(describe_instances_request)

        # 根据传入的实例ID获取到对应的实例内网ip_address
        primary_ip_address = \
            result_ecs.body.instances.instance[0].network_interfaces.network_interface[0].primary_ip_address

        # 返回对应实例的内网地址
        return primary_ip_address

    # 修改对应虚拟服务器组的配置
    def set_weight(self, vserver_group_id: str, vserver_group_name: str, backend_servers: str) -> None:
        # 建client认证
        client = self.create_client('Slb')

        # 传入的backend_servers需要为[{"Type": "ecs", "Weight": xx, "Port": xx, "ServerId": "i-xx"}]格式的string
        set_vserver_group_attribute_request = slb_20140515_models.SetVServerGroupAttributeRequest(
            region_id=self.Ali_Cloud['Region_ID'],
            vserver_group_id=vserver_group_id,
            vserver_group_name=vserver_group_name,
            backend_servers=backend_servers)
        set_weight_result = client.set_vserver_group_attribute(set_vserver_group_attribute_request)
        # logging.info(f'{set_weight_result.body}')
        return set_weight_result.body

    def main(self) -> set_weight:
        client = self.create_client(what_is='Slb')
        describe_vserver_groups_request = slb_20140515_models.DescribeVServerGroupsRequest(
            load_balancer_id=self.load_balancer_id,
            region_id=self.Ali_Cloud['Region_ID'])

        # 通过阿里云的API获取对应SLB的虚拟服务器组的详细信息
        for backend_server in client.describe_vserver_groups(describe_vserver_groups_request).body.vserver_groups.\
                vserver_group:

            # 判断虚拟服务器组的name和传入的值是否相同
            if backend_server.vserver_group_name == self.Project_Name:
                # 获得与传入项目名相同的虚拟服务器组name的group_name，修改权重需要此参数
                vserver_group_name = backend_server.vserver_group_name

                # 获得与传入项目名相同的虚拟服务器组name的group_id，修改权重需要此参数
                vserver_group_id = backend_server.vserver_group_id
                describe_vserver_group_attribute_request = slb_20140515_models.DescribeVServerGroupAttributeRequest(
                        region_id=self.Ali_Cloud['Region_ID'], vserver_group_id=backend_server.vserver_group_id)

                # 通过describe_vserver_group_attribute获取查询虚拟服务器组详细信息
                result_slb = client.describe_vserver_group_attribute(describe_vserver_group_attribute_request)

                # 将获取的结果转为list[json]方便后面调用和修改值，因为json需要" replace转化' -> "
                vserver_list = [json.loads(str(list_vserver).replace('\'', '\"'))
                                for list_vserver in result_slb.body.backend_servers.backend_server]

                # 无论虚拟服务器组中有多少机器，都会循环一遍
                for vserver_num in range(len(vserver_list)):

                    # 获得对应的json
                    vserver = vserver_list[vserver_num]

                    # 设置初始权重值，方便后面上流量
                    initial_weight = vserver["Weight"]

                    # 将虚拟服务器组的对应虚拟服务器权重设置为0，实现下流量
                    vserver_list[vserver_num]["Weight"] = 0

                    # 获取对应虚拟服务器的ServerId 再通过ecs_ip查询对应的内网IP，再通过ECS内网IP进行部署操作
                    server_id = vserver["ServerId"]
                    logging.info(f'{self.ecs_ip(server_id)},开始部署。\n')
                    logging.info(f'{self.ecs_ip(server_id)} 服务器切出,初始权重:{initial_weight}')
                    self.set_weight(vserver_group_id, vserver_group_name, str(vserver_list))

                    # 开始部署操作
                    # 开始部署操作
                    # 开始部署操作
                    deploy_action = DeployMain(remote_ip=str(self.ecs_ip(server_id)), env=self.ENV,
                                               project_name=self.Project_Name, package_name=self.Package_Name,
                                               env_json=self.Env_json)
                    # 执行远程备份操作，将旧的二进制文件进行备份，便于回滚
                    deploy_action.backup()
                    # 同步压缩包、二进制文件， 如果是压缩包会自动解压、删除压缩包
                    deploy_action.sync_file()
                    # 远程部署服务器上的服务重启，不同项目需要在Service_Deploy restart中增加json
                    if deploy_action.restart_service():
                        logging.info(f'远程服务器：{self.ecs_ip(server_id)} 健康检查正常！')
                    else:
                        logging.error(f'远程服务器：{self.ecs_ip(server_id)} 健康接口检查失败！终止操作！')
                        exit(999)

                    # 部署完成后，进行权重恢复初始值，完成上流量
                    time.sleep(2)
                    vserver["Weight"] = int(initial_weight)
                    self.set_weight(vserver_group_id, vserver_group_name, str(vserver_list))
                    logging.info(f'{self.ecs_ip(server_id)} 服务器切入，当前权重为：{initial_weight}')
                    logging.info(f'{self.ecs_ip(server_id)},部署成功。\n')


if __name__ == '__main__':
    Env = sys.argv[1]
    Project_Name = sys.argv[2]
    Package_Name = sys.argv[3]

    a = Sample(env=Env, project_name=Project_Name, package_name=Package_Name)
    a.main()
